'''
File: custom_steppables.py
Author: Niklas Semmler
Description: customized steppables for the simulation of the plasmodium phase of the Physarum polycephalum 
'''
#!/usr/bin/python

import sys
import random
from PySteppables import * 
import CompuCell
from XMLUtils import dictionaryToMapStrStr as d2mss
import math

class SecretionSteppable(SteppableBasePy):
    ''' defines additional waves released by random food sources every periode'''
    def __init__(self,_simulator,_frequency=50):
        SteppableBasePy.__init__(self,_simulator, _frequency)
        self.runBeforeMCS=1
        self.counter = 1 
        self.hbound = 5
    
    def step(self,mcs):
        attrSecretor = self.getFieldSecretor("ATTR")
        for cell in self.cellList:
            cell.onset = 0
            if cell.type == 1 and self.counter == random.randint(1,self.hbound):
                attrSecretor.secreteInsideCell(cell,200)
        
        self.counter += 1
        if self.counter > self.hbound:
            self.counter = 1

class VolumeAnnealingSteppable(SteppableBasePy):
    ''' volumen is changed every 100mcs '''
    def __init__(self,_simulator,_frequency=25):
        ''' define constants dependent on _simulator'''
        SteppableBasePy.__init__(self, _simulator, _frequency)
        self.simulator = _simulator
        self.nSteps = _simulator.getNumSteps()
        self.inventory = self.simulator.getPotts().getCellInventory()
        self.cellList = CellList(self.inventory)

    def start(self):
        ''' defines number of parameters for volume control'''
        self.foodsources = [] # foodsources
        self.FSVolume = 0 # cumulative volume added by foodsources
        self.addFSVolume = 175 # volume added per connected foodsource
        self.baseVolume = 9*self.addFSVolume # base volume of the slime mold
        self.basePercVolume = 0.2 # base percentage of the volume 
        self.oscillPercVolume = 0.2# percentage of the volume oscillating
        self.devPercVolume = 1.2-(self.basePercVolume + 2*self.oscillPercVolume) # little more than the rest volume used for growth and shrinking
        self.growthPeriode = (2*self.nSteps)/(3*self.frequency)
        self.shrinkPeriode = (self.nSteps/self.frequency) - self.growthPeriode
        self.growthPercVolume = self.devPercVolume / self.growthPeriode # growth per time unit
        self.shrinkPercVolume = -(self.devPercVolume/2) / self.shrinkPeriode # growth per time unit
        self.chanPercVolume = self.growthPercVolume
        self.oscillFreq = 1000.0 # number of mcs between 0 and 2 pi of a sine cycle.

        for cell in self.cellList:
            if cell.type == 1:
                self.foodsources += [cell]
            elif cell.type == 2:
                self.slimey = cell # single cell slime mold
        
        # first definition of the slime mold's volume constraint
        self.slimey.targetVolume = self.baseVolume * self.basePercVolume
        self.slimey.lambdaVolume = 6

    def step(self, mcs):
        # if growth periode is over shrink!
        if mcs/self.frequency > self.growthPeriode:
            self.chanPercVolume = self.shrinkPercVolume
        
        self.volumeDev() # affects self.basePercVolume
        self.relToFS() # affects self.FSVolume
        oscillPerc = self.volumeOscill(mcs)
        
        # integrate oscillation, growth and base percentage
        perc = oscillPerc + self.basePercVolume
        # integratie base and additional volumes
        vol  = self.baseVolume + self.FSVolume

        # calculate new combined target volume
        self.slimey.targetVolume = perc * vol

    def volumeOscill(self,mcs):
        ''' create the oscillation of the slime mold '''
        per = math.sin((mcs/self.oscillFreq)*2*math.pi)+1
        print "Oscillation: ", per
        return per * self.oscillPercVolume
    
    def volumeDev(self):
        ''' develop volume (either growth or shrinkage)'''
        self.basePercVolume += self.chanPercVolume
        print "Growth: ", self.basePercVolume
    
    def relToFS(self):
        ''' find the number of connected foodsources and calc additional volume '''
        self.FSVolume = 0
        for cell in self.foodsources:
            nbrlist = self.getCellNeighbors(cell)
            if nbrlist is not None:
                for nbr in nbrlist:
                    if nbr.neighborAddress:
                        if nbr.neighborAddress.type == 2:
                            self.FSVolume += self.addFSVolume
